import * as THREE from 'three'
import * as d3 from 'd3'
import { CSS2DRenderer, CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import * as TWEEN from '@tweenjs/tween.js'

// 获取区块中心点
export const setCenter = (map) => {
  map.rotation.x = -Math.PI / 2
  const box = new THREE.Box3().setFromObject(map)
  const center = box.getCenter(new THREE.Vector3())

  const offset = [0, 0]
  map.position.x = map.position.x - center.x - offset[0]
  map.position.z = map.position.z - center.z - offset[1]
}

// 创建边界线
export const createLine = (data, mapConfig, offsetXY) => {
  const points = [] as any
  data.forEach((item) => {
    const [x, y] = offsetXY(item)
    points.push(new THREE.Vector3(x, -y, 0))
  })
  const lineGeometry = new THREE.BufferGeometry().setFromPoints(points)
  const uplineMaterial = new THREE.LineBasicMaterial({
    color: mapConfig.lineColor
    // linewidth: 5
  })
  // const downlineMaterial = new THREE.LineBasicMaterial({
  //   color: '#00d5ff',
  //   linewidth: 5
  // })

  // 底部边界线有宽度，不知道为什么上面的 linewidth 不生效，所以用 TubeGeometry 模拟
  const path = new THREE.CatmullRomCurve3(points)
  const tubeGeometry = new THREE.TubeGeometry(path, 64, mapConfig.bottomLine.radius, 8, false)
  const material = new THREE.MeshBasicMaterial({ color: mapConfig.bottomLine.color })
  const downTube = new THREE.Mesh(tubeGeometry, material)
  downTube.name = 'downTube'
  const upLine = new THREE.Line(lineGeometry, uplineMaterial)
  // const downLine = new THREE.Line(lineGeometry, downlineMaterial)
  // downLine.position.z = -0.02
  // downLine.name = 'downLine'
  upLine.position.z = mapConfig.depth + 0.01
  upLine.name = 'upLine'
  return [upLine, downTube]
}

// 创建地图板块
export const createMesh = (data, mapConfig, offsetXY) => {
  const shape = new THREE.Shape()
  data.forEach((item, idx) => {
    const [x, y] = offsetXY(item)

    if (idx === 0) shape.moveTo(x, -y)
    else shape.lineTo(x, -y)
  })

  const geometry = new THREE.ExtrudeGeometry(shape, {
    depth: mapConfig.depth,
    bevelEnabled: false
  })
  // const mapUrl = new URL("./assets/img/tt.png", import.meta.url).href;
  // const normalUrl = new URL("./assets/img/fxtt.png", import.meta.url).href;
  // const map = new THREE.TextureLoader().load(mapUrl)
  // const normalMap = new THREE.TextureLoader().load(normalUrl)
  // map.repeat.set(0.1,0.1)
  // map.offset.set(0.5, 0.5);
  // normalMap.repeat.set(0.1,0.1)
  // normalMap.offset.set(0.5, 0.5);
  // const material = new THREE.MeshBasicMaterial({
  //   map,
  //   normalMap
  // })
  const material = new THREE.MeshStandardMaterial(mapConfig.material)
  const sideMaterial = new THREE.MeshBasicMaterial(mapConfig.sideMaterial)
  const mesh = new THREE.Mesh(geometry, [material, sideMaterial])
  mesh.position.set(0, 0, 0)
  mesh.name = 'map'
  return mesh
}

// 创建名称
export const createLabel = (name, point, mapConfig, offsetXY) => {
  const div = document.createElement('div')
  Object.entries(mapConfig.labelStyle).forEach(([key, value]) => {
    div.style[key] = value
  })
  div.textContent = name
  const label = new CSS2DObject(div)
  // label.scale.set(0.01, 0.01, 0.01);
  const [x, y] = offsetXY(point) //地图坐标系转换成空间坐标
  label.position.set(x, -y, mapConfig.depth + 0.2)
  return label
}

// 创建标记
const createIcon = (point, depth, offsetXY) => {
  const url = new URL('./assets/img/icon.png', import.meta.url).href
  const map = new THREE.TextureLoader().load(url)
  const material = new THREE.SpriteMaterial({
    map,
    transparent: true
  })
  const sprite = new THREE.Sprite(material)
  const [x, y] = offsetXY(point)
  sprite.scale.set(0.25, 0.25, 0.25)
  // sprite.position.set(x, -y, depth + 0.15);
  sprite.position.set(x + Math.random() - 0.5, -y + Math.random() - 0.5, depth + 0.15)
  sprite.renderOrder = 1

  return sprite
}

// 创建柱状图组
const creatBarGroup = (point, depth, name, offsetXY, barData, barConfig) => {
  const barItem = barData.find((item) => item.name === name)
  const max = Math.max(...barData.map((item) => item.value), ...barData.map((item) => item.value2))
  if (!barItem) {
    alert('柱状图数据有误')
    return
  }
  const group = new THREE.Group()
  group.name = 'bar-' + name
  // 创建圆柱体的几何体
  const height = (barConfig.height / max) * barItem.value // 计算高度
  const geometry = new THREE.CylinderGeometry(barConfig.radius, barConfig.radius, height, 32)
  // 创建材质
  const material = new THREE.MeshStandardMaterial(barConfig.material)
  // 创建圆柱体网格
  const cylinder = new THREE.Mesh(geometry, material)
  cylinder.name = 'cylinder1-' + name
  group.add(cylinder)

  let groupHeight = height

  // 第二根柱子
  if (barItem.value2) {
    const height2 = (barConfig.height / max) * barItem.value2 // 计算高度
    const geometry2 = new THREE.CylinderGeometry(barConfig.radius, barConfig.radius, height2, 32)
    const material2 = new THREE.MeshStandardMaterial(barConfig.material2)
    const cylinder2 = new THREE.Mesh(geometry2, material2)
    cylinder2.name = 'cylinder2-' + name
    cylinder2.position.x = 0.05
    if (height2 > height) {
      groupHeight = height2
      cylinder.position.y -= (height2 - height) / 2
    } else {
      cylinder2.position.y -= (height - height2) / 2
    }
    group.add(cylinder2)
  }

  // label 文字
  const div = document.createElement('div')
  Object.entries(barConfig.labelStyle).forEach(([key, value]) => {
    div.style[key] = value
  })
  div.innerHTML = `
    <div>${name}</div>
    <div>产值1：${barItem.value}万元</div>
    <div>产值2：${barItem.value2}万元</div>`
  const tip = new CSS2DObject(div)
  tip.name = 'tip-' + name
  tip.position.set(0.5, groupHeight / 2 + 0.5, 0)
  tip.visible = false
  // 给光柱添加数值
  group.add(tip)

  // 底部光晕
  const circlePlane = new THREE.PlaneGeometry(0.6, 0.6)
  const circlePlaneUrl = new URL('./assets/img/circlePlane.png', import.meta.url).href
  const circlePlaneTexture = new THREE.TextureLoader().load(circlePlaneUrl)
  const circlePlaneMaterial = new THREE.MeshBasicMaterial({
    color: '#fff',
    map: circlePlaneTexture,
    transparent: true,
    blending: THREE.AdditiveBlending,
    depthWrite: false,
    side: THREE.DoubleSide
  })
  const circlePlaneMesh = new THREE.Mesh(circlePlane, circlePlaneMaterial)
  circlePlaneMesh.rotation.x = -Math.PI / 2
  circlePlaneMesh.position.set(0, -groupHeight / 2 + 0.02, 0)
  circlePlaneMesh.name = 'circlePlane-' + name
  circlePlaneMesh.visible = false
  group.add(circlePlaneMesh)

  const [x, y] = offsetXY(point)
  group.rotation.x = Math.PI / 2
  group.position.set(x, -y, depth + groupHeight / 2)
  group.scale.set(0, 0, 0)

  new TWEEN.Tween(group.scale)
    .to({ x: 1, y: 1, z: 1 }, 1000) // 设置动画结束时的高度和动画时间
    .delay(3000 + Math.random() * 1000)
    .start()
    .easing(TWEEN.Easing.Quadratic.InOut) // 使用 Quadratic 缓动函数
    .onUpdate(function () {
      group.position.z = depth + (group.scale.y * groupHeight) / 2
    })

  return group
}

// 创建柱状图
// const createBar = (point, depth, name, offsetXY, barData, barConfig) => {
//   const barItem = barData.find((item) => item.name === name)
//   const max = Math.max(...barData.map((item) => item.value))
//   if (!barItem) {
//     alert('柱状图数据有误')
//     return
//   }
//   // 创建圆柱体的几何体
//   const height = barConfig.height / max * barItem.value // 计算高度
//   const geometry = new THREE.CylinderGeometry(barConfig.radius, barConfig.radius, height, 32);
//   // 创建材质
//   const material = new THREE.MeshStandardMaterial(barConfig.material)
//   // 创建圆柱体对象
//   const cylinder = new THREE.Mesh(geometry, material);
//   cylinder.name = 'bar-' + name
//   // 创建光柱数值
//   barConfig.labelStyle.show && window.setTimeout(() => {
//     const div = document.createElement("div");
//     Object.entries(barConfig.labelStyle).forEach(([key, value]) => {
//       div.style[key] = value
//     })
//     div.textContent = barConfig.labelFormatter(barItem.value)
//     const tip = new CSS2DObject(div);
//     tip.scale.set(0.01, 0.01, 0.01);
//     tip.position.set(0, height / 2 + 0.2, 0);
//     // 给光柱添加数值
//     cylinder.add(tip);
//   }, 2000 + Math.random() * 1000);

//   const [x, y] = offsetXY(point);
//   cylinder.rotation.x = Math.PI / 2;
//   cylinder.position.set(x, -y, depth + height / 2);
//   cylinder.scale.set(0, 0, 0)
//   // 使用 Tween.js 创建动画
//   new TWEEN.Tween(cylinder.scale)
//     .to({ x: 1, y: 1, z: 1 }, 1000) // 设置动画结束时的高度和动画时间
//     .delay(1500 + Math.random() * 1000)
//     .start()
//     .easing(TWEEN.Easing.Quadratic.InOut) // 使用 Quadratic 缓动函数
//     .onUpdate(function () {
//       cylinder.position.set(x, -y, depth + cylinder.scale.y * height / 2);
//       // cylinder2.position.set(x, -y, depth + cylinder.scale.y * height / 2);
//     })

//   // const cylinder2 = cylinder.clone()
//   // cylinder2.position.setX(x + 0.5)

//   // const group = new THREE.Group()
//   // group.add(cylinder, cylinder2)

//   return cylinder
// }

const createPlane = () => {
  // 创建CanvasTexture，用于绘制圆
  const canvas = document.createElement('canvas')
  const context = canvas.getContext('2d') as CanvasRenderingContext2D
  canvas.width = 512
  canvas.height = 512
  for (let i = 0; i < 4; i++) {
    // 调用beginPath()确保每个同心圆都有独立的路径
    context.beginPath()
    // 计算同心圆的半径
    const radius = 50 * (i + 1)
    // 在Canvas上绘制同心圆
    context.arc(256, 256, radius, 0, 2 * Math.PI)
    // 设置虚线样式
    context.setLineDash([5, 5])
    // 设置线宽
    context.lineWidth = 0.5
    // 设置圆的描边颜色
    context.strokeStyle = 'rgba(255, 255, 255, 0.1)'
    // 描边
    context.stroke()
  }
  const texture = new THREE.CanvasTexture(canvas)
  const planeGeometry = new THREE.PlaneGeometry(10, 10)
  // 将平面放置在 z = 0 处
  planeGeometry.rotateZ(-Math.PI / 2) // 旋转平面使其法线方向朝上
  planeGeometry.translate(0, 0, -0.1) // 平移平面到 z = 0 处
  // 创建平面的材质
  // const mapUrl = new URL("./assets/img/tt.png", import.meta.url).href;
  // const normalUrl = new URL("./assets/img/fxtt.png", import.meta.url).href;
  // const map = new THREE.TextureLoader().load(mapUrl)
  // const normalMap = new THREE.TextureLoader().load(normalUrl)
  const planeMaterial = new THREE.MeshBasicMaterial({
    // color: '#999',
    map: texture,
    // normalMap,
    side: THREE.DoubleSide,
    transparent: true,
    opacity: 0.5
  }) // 设置平面的颜色和渲染双面
  // 创建平面的网格
  const plane = new THREE.Mesh(planeGeometry, planeMaterial)
  plane.name = 'plane'
  return plane
}

// 创建地图
export const createMap = (geoData, barData, mapConfig, barConfig) => {
  const map = new THREE.Object3D()
  const center = geoData.features[0].properties.centroid
  // 将经纬度解析为空间坐标
  const offsetXY = d3.geoMercator()
  offsetXY.center(center).translate([0, 0])
  geoData.features.forEach((feature) => {
    const unit = new THREE.Object3D()
    const { centroid, center, name } = feature.properties
    const { coordinates, type } = feature.geometry
    const point = centroid || center || [0, 0]

    // const color = new THREE.Color(`hsl(${233},${Math.random() * 30 + 55}%,${Math.random() * 30 + 55}%)`).getHex()
    // const depth = Math.random() * 0.6 + 0.3
    const depth = mapConfig.depth //定义地图高度

    const label = createLabel(name, point, mapConfig, offsetXY)
    const icon = createIcon(center, depth, offsetXY) //创建图标
    // const bar = createBar(center, depth, name, offsetXY, barData, barConfig) //创建柱状图
    const barGroup = creatBarGroup(center, depth, name, offsetXY, barData, barConfig) //创建柱状图组
    const plane = createPlane()

    coordinates.forEach((coordinate) => {
      if (type === 'MultiPolygon') coordinate.forEach((item) => fn(item))
      if (type === 'Polygon') fn(coordinate)

      function fn(coordinate) {
        unit.name = name
        const mesh = createMesh(coordinate, mapConfig, offsetXY)
        const line = createLine(coordinate, mapConfig, offsetXY)
        unit.add(mesh, ...line)
      }
    })
    map.add(unit, label, barGroup, plane)
    setCenter(map)
  })
  return map
}

// 创建控制器
export const createControls = (camera, renderer) => {
  const controls = new OrbitControls(camera, renderer.domElement)
  // 设置控制器阻尼
  controls.enableDamping = true
  controls.update()
  //上下翻转的最大角度
  controls.maxPolarAngle = 1.2
  //上下翻转的最小角度
  controls.minPolarAngle = 0
  // 禁止相机平移
  controls.enablePan = false
  return controls
}

// 创建 labelRender
export const createLabelRenderer = () => {
  const labelRenderer = new CSS2DRenderer()
  labelRenderer.domElement.style.position = 'absolute'
  labelRenderer.domElement.style.top = '0px'
  labelRenderer.domElement.style.pointerEvents = 'none'
  labelRenderer.setSize(window.innerWidth, window.innerHeight)
  return labelRenderer
}

// 创建场景
export const createScene = () => {
  const scene = new THREE.Scene()
  // scene.background = new THREE.Color('#999')

  const axesHelper = new THREE.AxesHelper(100)
  scene.add(axesHelper)

  return scene
}

// 创建相机
export const createCamera = () => {
  const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
  camera.position.set(0, 500, 0)
  camera.lookAt(0, 0, 0)

  // 设置进入的动画
  new TWEEN.Tween(camera.position).to({ x: 120, y: 180, z: 200 }, 3000).start().easing(TWEEN.Easing.Sinusoidal.InOut) //进入和结束都设置缓动

  return camera
}

// 创建渲染器
export const createRenderer = () => {
  const renderer = new THREE.WebGLRenderer({
    alpha: true, // 设置抗锯齿
    antialias: true,
    // 设置物理灯光模拟效果
    physicallyCorrectLights: true,
    // 设置对数深度缓冲区
    logarithmicDepthBuffer: true
  })
  // 设置渲染尺寸大小
  renderer.setSize(window.innerWidth, window.innerHeight)
  return renderer
}

// 初始化光源
export const initLights = (scene) => {
  const ambientLight = new THREE.AmbientLight(0xd4e7fd, 4)
  scene.add(ambientLight)
  const directionalLight = new THREE.DirectionalLight(0xe8eaeb, 0.2)
  directionalLight.position.set(0, 30, 50)
  const directionalLight2 = directionalLight.clone()
  directionalLight2.position.set(0, 30, -50)
  const directionalLight3 = directionalLight.clone()
  directionalLight3.position.set(-50, 30, 0)
  const directionalLight4 = directionalLight.clone()
  directionalLight4.position.set(50, 30, 0)
  const directionalLight5 = directionalLight.clone()
  directionalLight5.position.set(35, 60, 50)
  const directionalLight6 = directionalLight.clone()
  directionalLight6.position.set(-35, 60, 50)
  const directionalLight7 = directionalLight.clone()
  directionalLight7.position.set(-35, 60, -50)
  const directionalLight8 = directionalLight.clone()
  directionalLight8.position.set(25, 50, -50)
  const directionalLight9 = directionalLight.clone()
  directionalLight9.position.set(0, 60, -60)
  const directionalLight10 = directionalLight.clone()
  directionalLight10.position.set(0, 60, 40)
  scene.add(directionalLight)
  scene.add(directionalLight2)
  scene.add(directionalLight3)
  scene.add(directionalLight4)
  scene.add(directionalLight5)
  scene.add(directionalLight6)
  scene.add(directionalLight7)
  scene.add(directionalLight8)
  scene.add(directionalLight9)
  scene.add(directionalLight10)
}

// 渲染地图和数据
export const renderMap = (scene, camera, tooltip, geoData, barData, mapConfig, barConfig) => {
  //获取地图边界
  const map = createMap(geoData, barData, mapConfig, barConfig)
  map.scale.set(25, 25, 25)
  scene.add(map)

  // 处理鼠标事件
  // window.addEventListener('mousemove', (event) => {
  //   const mouse = new THREE.Vector2()
  //   //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
  //   mouse.x = (event.clientX / window.innerWidth) * 2 - 1
  //   mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
  //   const raycaster = new THREE.Raycaster()
  //   // 通过摄像机和鼠标位置更新射线
  //   // 计算物体和射线的焦点
  //   raycaster.setFromCamera(mouse, camera)
  //   const intersects = raycaster.intersectObjects(map.children).filter((item) => item.object.type !== 'Line')
  //   if (intersects.length > 0) {
  //     if (intersects[0].object.parent.name !== '' && intersects[0].object.name === 'map') {
  //       showTooltip(tooltip, intersects[0].object.parent.name)
  //     } else if (intersects[0].object.parent.name.includes('bar-')) {
  //       const name = intersects[0].object.parent.name.split('-')[1]
  //       showTooltip(tooltip, name)
  //     } else if (intersects[0].object.name === 'plane') {
  //       tooltip.style.display = 'none'
  //     }
  //   } else {
  //     // 出去了，就初始化一下
  //     tooltip.style.display = 'none'
  //   }
  //   function showTooltip(tooltip, name) {
  //     tooltip.style.display = 'block'
  //     tooltip.style.left = event.clientX + 10 + 'px'
  //     tooltip.style.top = event.clientY + 10 + 'px'
  //     const barItem = barData.find((item) => item.name === name)
  //     tooltip.innerHTML = `
  //         <div>${name}</div>
  //         <div>产值1：${barItem.value}万元</div>
  //         <div>产值2：${barItem.value2}万元</div>`
  //   }
  // })

  // 轮播
  let idx = 0
  let pause = false // 是否暂停
  const barGroup = map.children.filter((item) => item.name.includes('bar-'))
  const setHighLight = (idx) => {
    barGroup.forEach((item) => {
      item.children.forEach((child) => {
        if (child.name.includes('tip-')) child.visible = false
        if (child.name.includes('circlePlane-')) child.visible = false
        if (child.name.includes('cylinder1-')) child.material.emissive.set(0x000000)
        if (child.name.includes('cylinder2-')) child.material.emissive.set(0x000000)
      })
    })
    barGroup[idx].children.forEach((item) => {
      if (item.name.includes('tip-')) item.visible = true
      if (item.name.includes('circlePlane-')) item.visible = true
      if (item.name.includes('cylinder1-')) item.material.emissive.set('#fab005')
      if (item.name.includes('cylinder2-')) item.material.emissive.set('#00d5ff')
    })
  }
  const timer = window.setInterval(() => {
    if (pause) return
    setHighLight(idx)
    idx++
    if (idx === barGroup.length) idx = 0
  }, 3000)

  // 鼠标事件
  window.addEventListener('mousemove', (event) => {
    const mouse = new THREE.Vector2()
    //将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1)
    mouse.x = (event.clientX / window.innerWidth) * 2 - 1
    mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
    const raycaster = new THREE.Raycaster()
    // 通过摄像机和鼠标位置更新射线
    // 计算物体和射线的焦点
    raycaster.setFromCamera(mouse, camera)
    const intersects = raycaster.intersectObjects(map.children).filter((item) => item.object.type !== 'Line')
    if (intersects.length > 0) {
      if (intersects[0].object.parent.name !== '' && intersects[0].object.name === 'map') {
        pause = true
        const name = intersects[0].object.parent.name
        const groupIndex = barGroup.findIndex((item) => item.name === 'bar-' + name)
        setHighLight(groupIndex)
      } else if (intersects[0].object.parent.name.includes('bar-')) {
        pause = true
        const name = intersects[0].object.parent.name.split('-')[1]
        const groupIndex = barGroup.findIndex((item) => item.name === 'bar-' + name)
        setHighLight(groupIndex)
      } else if (intersects[0].object.name === 'plane') {
        pause = false
      }
    } else {
      pause = false
    }
  })
}
